//
//  Copyright (C) 1998-2007 J. Andrew McLaughlin
// 
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of the GNU General Public License as published by the Free
//  Software Foundation; either version 2 of the License, or (at your option)
//  any later version.
// 
//  This program is distributed in the hope that it will be useful, but
//  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
//  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
//  for more details.
//  
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//

#include "kernelMain.h"
#include "kernelEnvironment.h"
#include "kernelError.h"
#include "kernelInitialize.h"
#include "kernelLoader.h"
#include "kernelMalloc.h"
#include "kernelMisc.h"
#include "core1/kernelMultitasker.h"
#include "kernelParameters.h"
#include "mach/processor.h"
#include "kernelSysTimer.h"
#include "kernelText.h"
#include <string.h>

// This is the global 'errno' error status variable for the kernel
int errno = 0;

// A copy of the OS loader info structure
static loaderInfoStruct osLoaderInfo;
loaderInfoStruct *kernelOsLoaderInfo = &osLoaderInfo;

// General kernel configuration variables
static variableList variables;
variableList *kernelVariables = &variables;


void kernelMain(unsigned kernelMemory, loaderInfoStruct *info)
{
	int status = 0;
	int pid = -1;
	char *value = NULL;

	// Copy the OS loader info structure into kernel memory
	kernelMemCopy(info, kernelOsLoaderInfo, sizeof(loaderInfoStruct));

	// Call the kernel initialization routine
	status = kernelInitialize(kernelMemory);
	if (status < 0)
	{
		// Kernel initialization failed. We don't exactly know
		// what failed. That makes it a little bit risky to call the 
		// error routine, but we'll do it anyway.

		kernelError("Initialization failed.  Press any key (or the \"reset\" button) to reboot.");

		// Do a loop, manually polling the keyboard input buffer
		// looking for the key press to reboot.
		while (kernelTextInputCount() == 0);

		kernelTextPrint("Rebooting...");
		kernelSysTimerWaitTicks(20); // Wait 2 seconds
		kernelProcessorReboot();
	}

	// Set any default environment variables
	kernelEnvironmentSet("PATH", DEFAULT_PATH);

	if (kernelVariables != NULL)
	{
		value = kernelMalloc(128);
		if (value)
		{
			// Find out which initial program to launch
			kernelVariableListGet(kernelVariables, "start.program", value, 128);

			// If the start program is our standard login program, use a custom
			// function to launch a login process 
			if (strncmp(value, DEFAULT_KERNEL_STARTPROGRAM, 128))
			{
				// Try to load the login process
				pid = kernelLoaderLoadProgram(value, PRIVILEGE_SUPERVISOR);
				if (pid < 0)
					// Don't fail, but make a warning message
					kernelWarning("Couldn't load start program \"%s\"", value);
				else
				{
					// Attach the start program to the console text streams
					kernelMultitaskerDuplicateIO(KERNELPROCID, pid, 1); // Clear

					// Execute the start program.  Don't block.
					kernelLoaderExecProgram(pid, 0);
				}
			}
			kernelFree(value);
		}
    }

	// If the kernel config file wasn't found, or the start program wasn't
	// specified, or loading the start program failed, assume we're going to
	// use the standard default login program
	if (pid < 0)
		kernelConsoleLogin();

	while(1)
	{
		// Finally, we will change the kernel state to 'sleeping'.  This is
		// done because there's nothing that needs to be actively done by
		// the kernel process itself; it just needs to remain resident in
		// memory.  Changing to a 'sleeping' state means that it won't get
		// invoked again by the scheduler.
		status = kernelMultitaskerSetProcessState(KERNELPROCID, proc_sleeping);
		if (status < 0)
			kernelError("The kernel process could not go to sleep.");

		// Yield the rest of this time slice back to the scheduler
		kernelMultitaskerYield();

		// We should never get here.  But we put it inside a while loop anyway.
		kernelError("The kernel was unexpectedly woken up");
	}
}
